package com.engine.salary.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.api.formmode.mybatis.util.SqlProxyHandle;
import com.cloudstore.dev.api.util.Util_DataCache;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.component.WeaTableColumnGroup;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.salary.constant.SalaryDefaultTenantConstant;
import com.engine.salary.constant.SalaryItemConstant;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.report.po.SalaryAcctResultReportPO;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctConfig;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctEmployeeBO;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctResultBO;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctResultLogBO;
import com.engine.salary.entity.salaryacct.dto.SalaryAccEmployeeListDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctImportFieldDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctResultTemplateDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryComparisonResultListDTO;
import com.engine.salary.entity.salaryacct.param.*;
import com.engine.salary.entity.salaryacct.po.*;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salarysob.bo.SalarySobItemAggregateBO;
import com.engine.salary.entity.salarysob.dto.SalaryApprovalDTO;
import com.engine.salary.entity.salarysob.dto.SalarySobEmpFieldDTO;
import com.engine.salary.entity.salarysob.dto.SalarySobItemAggregateDTO;
import com.engine.salary.entity.salarysob.po.SalarySobEmpFieldPO;
import com.engine.salary.entity.salarysob.po.SalarySobItemGroupPO;
import com.engine.salary.entity.salarysob.po.SalarySobItemPO;
import com.engine.salary.entity.salarysob.po.SalarySobPO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.UserStatusEnum;
import com.engine.salary.enums.salaryaccounting.LockStatusEnum;
import com.engine.salary.enums.salaryaccounting.SalaryAcctResultDataSourceEnum;
import com.engine.salary.enums.salaryitem.SalaryDataTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.sys.SalarySysConfMapper;
import com.engine.salary.service.*;
import com.engine.salary.sys.constant.SalarySysConstant;
import com.engine.salary.sys.entity.po.SalarySysConfPO;
import com.engine.salary.sys.enums.OpenEnum;
import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.*;
import com.engine.salary.util.db.IdGenerator;
import com.engine.salary.util.excel.ExcelParseHelper;
import com.engine.salary.util.excel.ExcelPreviewDTO;
import com.engine.salary.util.excel.ExcelSupport;
import com.engine.salary.util.excel.ExcelUtilPlus;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.ValidUtil;
import com.engine.salary.wrapper.SalarySobItemWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StopWatch;
import weaver.file.ImageFileManager;
import weaver.hrm.User;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.engine.salary.sys.constant.SalarySysConstant.OPEN_ACCT_RESULT_SUM;
import static com.engine.salary.sys.constant.SalarySysConstant.SALARY_ACCT_FIXED_COLUMNS;
import static com.engine.salary.util.excel.ExcelSupport.EXCEL_TYPE_XLSX;

/**
 * 薪资核算导入导出
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
@Slf4j
public class SalaryAcctExcelServiceImpl extends Service implements SalaryAcctExcelService {

    private SalaryAcctRecordService getSalaryAcctRecordService(User user) {
        return (SalaryAcctRecordService) ServiceUtil.getService(SalaryAcctRecordServiceImpl.class, user);
    }

    private SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) {
        return (SalaryAcctEmployeeService) ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
    }

    private SalaryAcctResultService getSalaryAcctResultService(User user) {
        return (SalaryAcctResultService) ServiceUtil.getService(SalaryAcctResultServiceImpl.class, user);
    }

    private SalarySobEmpFieldService getSalarySobEmpFieldService(User user) {
        return (SalarySobEmpFieldService) ServiceUtil.getService(SalarySobEmpFieldServiceImpl.class, user);
    }

    private SalarySobItemService getSalarySobItemService(User user) {
        return (SalarySobItemService) ServiceUtil.getService(SalarySobItemServiceImpl.class, user);
    }

    private SalaryItemService getSalaryItemService(User user) {
        return (SalaryItemService) ServiceUtil.getService(SalaryItemServiceImpl.class, user);
    }

    private SalaryComparisonResultService getSalaryComparisonResultService(User user) {
        return (SalaryComparisonResultService) ServiceUtil.getService(SalaryComparisonResultServiceImpl.class, user);
    }

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }


    private SalaryEmployeeService getSalaryEmployeeService(User user) {
        return (SalaryEmployeeService) ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }

    private SalarySobItemGroupService getSalarySobItemGroupService(User user) {
        return ServiceUtil.getService(SalarySobItemGroupServiceImpl.class, user);
    }

    private SalaryAcctReportService getSalaryAcctReportService(User user) {
        return ServiceUtil.getService(SalaryAcctReportServiceImpl.class, user);
    }

    private SalarySysConfMapper getSalarySysConfMapper() {
        return SqlProxyHandle.getProxy(SalarySysConfMapper.class);
    }

    private SalarySysConfService getSalarySysConfService(User user) {
        return ServiceUtil.getService(SalarySysConfServiceImpl.class, user);
    }

    private SalaryAcctResultLogService getSalaryAcctResultLogService(User user) {
        return ServiceUtil.getService(SalaryAcctResultLogServiceImpl.class, user);
    }

    private SalarySobItemWrapper getSalarySobItemWrapper(User user) {
        return ServiceUtil.getService(SalarySobItemWrapper.class, user);
    }

    private SalaryAcctResultTemplateService getSalaryAcctResultTemplateService(User user) {
        return ServiceUtil.getService(SalaryAcctResultTemplateServiceImpl.class, user);
    }

    private SalaryAcctSobConfigService getSalaryAcctSobConfigService(User user) {
        return ServiceUtil.getService(SalaryAcctSobConfigServiceImpl.class, user);
    }

    private SalaryApprovalRuleService getSalaryApprovalRuleService(User user) {
        return ServiceUtil.getService(SalaryApprovalRuleServiceImpl.class, user);
    }

    @Override
    public XSSFWorkbook exportSalaryAcctEmployee(SalaryAcctEmployeeQueryParam queryParam) {
        ValidUtil.doValidator(queryParam);

        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "薪资核算记录不存在或已被删除"));
        }
        // 1.工作簿名称
        String sheetName = SalaryI18nUtil.getI18nLabel(85368, "核算人员范围");
        // 2.表头
        String[] header = {
                SalaryI18nUtil.getI18nLabel(85429, "姓名"),
                SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"),
                SalaryI18nUtil.getI18nLabel(86185, "部门"),
                SalaryI18nUtil.getI18nLabel(86186, "手机号"),
                SalaryI18nUtil.getI18nLabel(86186, "工号"),
                SalaryI18nUtil.getI18nLabel(91075, "员工状态"),
                SalaryI18nUtil.getI18nLabel(91075, "入职日期")
//                SalaryI18nUtil.getI18nLabel(91075, "离职日期")
        };
        List<Object> headerList = new ArrayList<>(Arrays.asList(header));
        // 查询薪资核算人员
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getSalaryAcctEmployeeService(user).listByParam(queryParam);

        // 组装数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);

        // 3.表数据
        List<List<Object>> lists = convert2ExcelRow(salaryAcctEmployees);
        rows.addAll(lists);

        // 记录日志
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        String targetName = salarySobPO.getName() + ":" + SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth());
        SalaryLoggerUtil.recordExportSingleLog(SalaryElogConfig.salaryAcctRecordLoggerTemplate,
                queryParam.getSalaryAcctRecordId(),
                targetName,
                SalaryI18nUtil.getI18nLabel(0, "导出薪资核算人员"),
                SalaryI18nUtil.getI18nLabel(0, "导出薪资核算人员") + ":" + targetName,
                user
        );

//        return ExcelUtil.genWorkbookV2(rows, sheetName);
        return ExcelUtilPlus.genWorkbookV2(rows, sheetName);
    }


    @Override
    public XSSFWorkbook exportReducedEmployee(SalaryAcctEmployeeQueryParam queryParam) {
        ValidUtil.doValidator(queryParam);
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "薪资核算记录不存在或已被删除"));
        }

        // 1.工作簿名称
        String sheetName = SalaryI18nUtil.getI18nLabel(85368, "环比上月减少人员");
        // 2.表头
        String[] header = {
                SalaryI18nUtil.getI18nLabel(85429, "姓名"),
                SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"),
                SalaryI18nUtil.getI18nLabel(86185, "部门"),
                SalaryI18nUtil.getI18nLabel(86186, "手机号"),
                SalaryI18nUtil.getI18nLabel(86186, "工号"),
                SalaryI18nUtil.getI18nLabel(91075, "员工状态"),
                SalaryI18nUtil.getI18nLabel(91075, "入职日期")
//                SalaryI18nUtil.getI18nLabel(91075, "离职日期")
        };
        List<Object> headerList = new ArrayList<>(Arrays.asList(header));
        // 查询薪资核算人员（环比上月减少）
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getSalaryAcctEmployeeService(user).listByParam4Reduce(queryParam);

        // 组装数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);

        // 3.表数据
        List<List<Object>> lists = convert2ExcelRow(salaryAcctEmployees);
        rows.addAll(lists);

        // 记录日志
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        String targetName = salarySobPO.getName() + ":" + SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth());
        SalaryLoggerUtil.recordExportSingleLog(SalaryElogConfig.salaryAcctRecordLoggerTemplate,
                queryParam.getSalaryAcctRecordId(),
                targetName,
                SalaryI18nUtil.getI18nLabel(0, "导出环比上月减少人员"),
                SalaryI18nUtil.getI18nLabel(0, "导出环比上月减少人员") + ":" + targetName,
                user
        );

//        return ExcelUtil.genWorkbookV2(rows, sheetName);
        return ExcelUtilPlus.genWorkbookV2(rows, sheetName);
    }

    @Override
    public XSSFWorkbook exportAddedEmployee(SalaryAcctEmployeeQueryParam queryParam) {
        ValidUtil.doValidator(queryParam);

        // 1.工作簿名称
        String sheetName = SalaryI18nUtil.getI18nLabel(85368, "环比上月增加人员");
        // 2.表头
        String[] header = {
                SalaryI18nUtil.getI18nLabel(85429, "姓名"),
                SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"),
                SalaryI18nUtil.getI18nLabel(86185, "部门"),
                SalaryI18nUtil.getI18nLabel(86186, "手机号"),
                SalaryI18nUtil.getI18nLabel(86186, "工号"),
                SalaryI18nUtil.getI18nLabel(91075, "员工状态"),
                SalaryI18nUtil.getI18nLabel(91075, "入职日期")
//                SalaryI18nUtil.getI18nLabel(91075, "离职日期")
        };
        List<Object> headerList = new ArrayList<>(Arrays.asList(header));
        // 查询薪资核算人员（环比上月增加）
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getSalaryAcctEmployeeService(user).listByParam4Add(queryParam);

        // 组装数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);

        // 3.表数据
        List<List<Object>> lists = convert2ExcelRow(salaryAcctEmployees);
        rows.addAll(lists);

        // 记录日志
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        String targetName = salarySobPO.getName() + ":" + SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth());
        LoggerContext loggerContext = new LoggerContext<>();
        loggerContext.setUser(user);
        loggerContext.setTargetId("" + salaryAcctRecordPO.getId());
        loggerContext.setTargetName(targetName);
        loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
        loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "导出环比增加人员"));
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "导出环比增加人员") + ":" + targetName);
        SalaryElogConfig.salaryAcctRecordLoggerTemplate.write(loggerContext);

        return ExcelUtilPlus.genWorkbookV2(rows, sheetName);
    }

    private List<List<Object>> convert2ExcelRow(List<SalaryAcctEmployeePO> salaryAcctEmployees) {
        if (CollectionUtils.isEmpty(salaryAcctEmployees)) {
            return Collections.emptyList();
        }
        List<SalaryAccEmployeeListDTO> salaryAccEmployeeListDTOS = new ArrayList<>();
        List<List<SalaryAcctEmployeePO>> partition = Lists.partition(salaryAcctEmployees, 1000);
        for (List<SalaryAcctEmployeePO> tempList : partition) {
            // 人员
            List<Long> employeeIds = tempList.stream().map(SalaryAcctEmployeePO::getEmployeeId).collect(Collectors.toList());
            List<DataCollectionEmployee> simpleEmployees = getSalaryEmployeeService(user).getEmployeeByIdsAll(employeeIds);
            // 个税扣缴义务人
            List<Long> taxAgentIds = tempList.stream().map(SalaryAcctEmployeePO::getTaxAgentId).distinct().collect(Collectors.toList());
            List<TaxAgentPO> taxAgents = getTaxAgentService(user).listByIds(taxAgentIds);
            // 转换成列表数据
            salaryAccEmployeeListDTOS.addAll(SalaryAcctEmployeeBO.convert2EmployeeListDTO(tempList, taxAgents, simpleEmployees));
        }
        List<List<Object>> rows = Lists.newArrayListWithExpectedSize(salaryAccEmployeeListDTOS.size());
        for (SalaryAccEmployeeListDTO dto : salaryAccEmployeeListDTOS) {
            List<Object> row = Lists.newArrayListWithExpectedSize(8);
            row.add(dto.getEmployeeName());
            row.add(dto.getTaxAgentName());
            row.add(dto.getDepartmentName());
            row.add(dto.getMobile());
            row.add(dto.getJobNum());
            row.add(dto.getStatus());
            row.add(dto.getHireDate());
//            row.add(dto.getDismissDate());
            rows.add(row);
        }
        return rows;
    }

    @Override
    public XSSFWorkbook exportSalaryAcctResult(SalaryAcctResultQueryParam queryParam) {
        ValidUtil.doValidator(queryParam);

        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        // excel导出的表头
        List<WeaTableColumnGroup> headerColumnGroup = Lists.newArrayList();
        // 查询列表的表头
        List<WeaTableColumnGroup> weaTableColumns = listWeaTableColumn(salaryAcctRecordPO);

        //判断是否按照自定义字段导出表头
        List<WeaTableColumnGroup> finalWeaTableColumns = new ArrayList<>();
        if (queryParam.getSalaryItemIds() != null) {
            //获取人员基本信息字段（汇总）
            Collection<Map<String, String>> empFieldList = getSalarySobItemWrapper(user).empFieldList();
            List<String> empFieldIdList = new ArrayList<>();
            empFieldList.forEach(f -> empFieldIdList.add(f.get("id")));
            // 必须选择导出所需的薪资项目
            if (CollectionUtils.isEmpty(queryParam.getSalaryItemIds())) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(99019, "参数错误，请选择导入模板所需的薪资项目"));
            }
            List<Object> headerRangeList = new ArrayList<>();
            List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(queryParam.getSalaryItemIds());
            for (SalaryItemPO salaryItemPO : salaryItemPOS) {
                headerRangeList.add(salaryItemPO.getId().toString());
            }
            for (WeaTableColumnGroup tableColumn : weaTableColumns) {
                WeaTableColumnGroup columnGroupItem = (WeaTableColumnGroup) tableColumn;
                if (columnGroupItem.getChildren() != null) {
                    List<WeaTableColumnGroup> childrenColumns = columnGroupItem.getChildren().stream().filter(f -> headerRangeList.contains(f.getColumn())).collect(Collectors.toList());
                    if (childrenColumns.size() > 0) {
                        columnGroupItem.setChildren(childrenColumns);
                        finalWeaTableColumns.add(columnGroupItem);
                    }
                } else if (empFieldIdList.contains(tableColumn.getColumn()) || headerRangeList.contains(tableColumn.getColumn())) {
                    finalWeaTableColumns.add(columnGroupItem);
                }

            }
        } else {
            finalWeaTableColumns = weaTableColumns;
        }
//        parseHeader(headerColumnGroup, weaTableColumns);
        parseHeader(headerColumnGroup, finalWeaTableColumns);
//        // excel导出的表头
//        List<Object> headerList = new ArrayList<>(weaTableColumns);
        List<Object> headerList = new ArrayList<>(finalWeaTableColumns);
        // 查询薪资核算结果
        List<Map<String, Object>> resultMapList = getSalaryAcctResultService(user).listByParam(queryParam);


        //合计
        boolean total = false;
        SalarySysConfPO openSum = getSalarySysConfService(user).getOneByCode(OPEN_ACCT_RESULT_SUM);
        if (openSum != null && StringUtils.isNotBlank(openSum.getConfValue()) && OpenEnum.parseByValue(openSum.getConfValue()) == OpenEnum.OPEN) {
            total = true;
            Map<String, Object> sumRow = getSalaryAcctResultService(user).sumRow(queryParam);
            if (sumRow != null) {
                sumRow.forEach((k, v) -> {
                    if (NumberUtils.isCreatable(v.toString())) {
                        sumRow.put(k, new BigDecimal(v.toString()));
                    }
                });
            }
            if (sumRow != null) {
                sumRow.put("taxAgentName", "总计");
                resultMapList.add(sumRow);
            }
        }

        // excel导出的数据
        String DATA_TYPE_SUFFIX = "_type";
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);
        for (Map<String, Object> map : resultMapList) {
            List<Object> row = Lists.newArrayListWithExpectedSize(headerColumnGroup.size());
            for (WeaTableColumnGroup weaTableColumn : headerColumnGroup) {
                String fieldType = (String) map.getOrDefault(weaTableColumn.getColumn().toString() + DATA_TYPE_SUFFIX, StringUtils.EMPTY);
                if (StringUtils.equals("number", fieldType) && map.get(weaTableColumn.getColumn()) != null && NumberUtil.isNumber(map.get(weaTableColumn.getColumn()).toString())) {
                    row.add(new BigDecimal(StringUtils.isBlank(map.get(weaTableColumn.getColumn()).toString()) ? "0" : map.get(weaTableColumn.getColumn()).toString()));
                } else {
                    row.add(map.getOrDefault(weaTableColumn.getColumn(), StringUtils.EMPTY));
                }

            }
            rows.add(row);
        }

        // 记录日志
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        String targetName = salarySobPO.getName() + ":" + SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth());
        SalaryLoggerUtil.recordExportSingleLog(SalaryElogConfig.salaryAcctRecordLoggerTemplate,
                queryParam.getSalaryAcctRecordId(),
                targetName,
                SalaryI18nUtil.getI18nLabel(0, "导出薪资核算结果"),
                SalaryI18nUtil.getI18nLabel(0, "导出薪资核算结果") + ":" + targetName,
                user
        );


        String sheetName = "薪资核算结果";
//        return ExcelUtil.genWorkbookV2(rows, sheetName, total);
        return ExcelUtilPlus.genWorkbookWithChildTitleColumnWithExcelFormat(rows, sheetName, total);
    }


    /**
     * 构建薪资核算结果列表的表头
     *
     * @param salaryAcctRecordPO 薪资核算记录
     * @return
     */
    public List<WeaTableColumnGroup> listWeaTableColumn(SalaryAcctRecordPO salaryAcctRecordPO) {
        // 是否是回算
        SalaryAcctRecordPO byId = getSalaryAcctRecordService(user).getById(salaryAcctRecordPO.getId());
        boolean isBackCalc = Objects.equals(byId.getBackCalcStatus(), 1);
        // 查询薪资账套下的薪资项目+员工信息字段
        SalarySobItemAggregateDTO salarySobItemAggregateDTO = getSalarySobItemService(user).getAggregateWithItemHideBySalarySobId(salaryAcctRecordPO.getId(), salaryAcctRecordPO.getSalarySobId(), isBackCalc);
        // 构建薪资核算结果列表表头
        List<WeaTableColumnGroup> columnList = SalaryAcctResultBO.buildTableColumns(salarySobItemAggregateDTO, ListUtils.emptyIfNull(salaryAcctRecordPO.getLockSalaryItemIds()));
        // 获取固定列头数
        SalarySysConfPO salaryAcctFixedColumns = getSalarySysConfMapper().getOneByCode(SALARY_ACCT_FIXED_COLUMNS);
        if (salaryAcctFixedColumns != null) {
            int fixedNum = NumberUtils.isCreatable(salaryAcctFixedColumns.getConfValue()) ? Integer.valueOf(salaryAcctFixedColumns.getConfValue()) : 3;
            if (fixedNum == 0) {
                fixedNum = 3;
            }
            for (int i = 0; i < fixedNum && i < columnList.size(); i++) {
                columnList.get(i).setFixed("left");
            }
        }
        return columnList;
    }

    /**
     * 报表表头
     *
     * @param salaryAcctRecordPO
     * @return
     */
    public Map<String, Object> listColumn(SalaryAcctRecordPO salaryAcctRecordPO) {
        // 查询薪资账套下的薪资项目+员工信息字段
        SalarySobItemAggregateDTO salarySobItemAggregateDTO = getSalarySobItemService(user).getAggregateBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        // 构建薪资核算结果列表表头
        return SalaryAcctResultBO.buildColumns(salarySobItemAggregateDTO);
    }

    /**
     * 解析表头
     *
     * @param headerList
     * @param weaTableColumns
     */
    private void parseHeader(List<WeaTableColumnGroup> headerList, List<WeaTableColumnGroup> weaTableColumns) {
        for (WeaTableColumnGroup weaTableColumnGroup : weaTableColumns) {
            if (CollectionUtils.isEmpty(weaTableColumnGroup.getChildren())) {
                headerList.add(weaTableColumnGroup);
                continue;
            }
            parseHeader(headerList, weaTableColumnGroup.getChildren());
        }
    }

    @Override
    public SalaryAcctImportFieldDTO getImportField(Long salaryAcctRecordId) {
        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }

        SalaryAcctConfig config = getSalaryAcctSobConfigService(user).getSalaryAcctConfig(salaryAcctRecordId);

        // 查询薪资核算记录所用的薪资账套的薪资项目副本
        List<SalarySobItemPO> salarySobItems = config.getSalarySobItems();
        salarySobItems = salarySobItems.stream().filter(salarySobItemPO -> salarySobItemPO.getItemHide() == null || salarySobItemPO.getItemHide() == 0).collect(Collectors.toList());
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItems, SalarySobItemPO::getSalaryItemId);
        // 查询薪资项目
        List<SalaryItemPO> salaryItems = getSalaryItemService(user).listByIds(salaryItemIds);
        Map<Long, SalaryItemPO> salaryItemMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getId);

        Map<Long, List<SalarySobItemPO>> salarySobItemPOMap = SalaryEntityUtil.group2Map(salarySobItems, SalarySobItemPO::getSalarySobItemGroupId);

        // 查询薪资账套的薪资项目分类
        List<SalarySobItemGroupPO> salarySobItemGroupPOS = config.getSalarySobItemGroups();
        // 对分组进行排序
        salarySobItemGroupPOS = SalaryAcctResultBO.sortGroup(salarySobItemGroupPOS);
        // 对分组内薪资项目排序
        SalaryAcctResultBO.sortItem(salarySobItemPOMap);
        // 根据账套分组封装薪资项目的值
        List<SalaryAcctImportFieldDTO.ImportFieldByGroupDTO> itemsByGroup = new ArrayList<>();
        for (SalarySobItemGroupPO groupPO : salarySobItemGroupPOS) {
            List<SalarySobItemPO> groupItems = salarySobItemPOMap.getOrDefault(groupPO.getId(), Collections.emptyList());
            List<SalaryAcctImportFieldDTO.ImportFieldDTO> items = groupItems.stream()
                    .map(salarySobItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
                            .salaryItemId(salarySobItemPO.getSalaryItemId())
                            .salaryItemName(Optional.ofNullable(salaryItemMap.get(salarySobItemPO.getSalaryItemId())).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY))
                            .dataType(Optional.ofNullable(salaryItemMap.get(salarySobItemPO.getSalaryItemId())).map(SalaryItemPO::getDataType).orElse(SalaryDataTypeEnum.NUMBER.getValue()))
                            .build())
                    .collect(Collectors.toList());

            itemsByGroup.add(SalaryAcctImportFieldDTO.ImportFieldByGroupDTO.builder()
                    .salarySobItemGroupId(groupPO.getId())
                    .salarySobItemGroupName(groupPO.getName())
                    .salaryItems(items)
                    .sortedIndex(groupPO.getSortedIndex()).build());
        }
        // 未分类
        List<SalarySobItemPO> noGroupItems = salarySobItemPOMap.getOrDefault(0L, Collections.emptyList());
        if (CollectionUtils.isNotEmpty(noGroupItems)) {
            List<SalaryAcctImportFieldDTO.ImportFieldDTO> items = noGroupItems.stream()
                    .map(salarySobItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
                            .salaryItemId(salarySobItemPO.getSalaryItemId())
                            .salaryItemName(Optional.ofNullable(salaryItemMap.get(salarySobItemPO.getSalaryItemId())).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY))
                            .dataType(Optional.ofNullable(salaryItemMap.get(salarySobItemPO.getSalaryItemId())).map(SalaryItemPO::getDataType).orElse(SalaryDataTypeEnum.NUMBER.getValue()))
                            .build())
                    .collect(Collectors.toList());

            itemsByGroup.add(SalaryAcctImportFieldDTO.ImportFieldByGroupDTO.builder()
                    .salarySobItemGroupId(0L)
                    .salarySobItemGroupName("未分类")
                    .salaryItems(items)
                    .sortedIndex(itemsByGroup.size()).build());
        }

//        // 薪资项目中的公式项
//        List<SalaryAcctImportFieldDTO.ImportFieldDTO> formulaItems = salaryItems.stream()
//                .filter(salaryItemPO -> Objects.equals(salaryItemPO.getValueType(), SalaryValueTypeEnum.FORMULA.getValue()))
//                .map(salaryItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
//                        .salaryItemId(salaryItemPO.getId())
//                        .salaryItemName(salaryItemPO.getName())
//                        .build())
//                .collect(Collectors.toList());
//        // 薪资项目中的SQl项
//        List<SalaryAcctImportFieldDTO.ImportFieldDTO> sqlItems = salaryItems.stream()
//                .filter(salaryItemPO -> Objects.equals(salaryItemPO.getValueType(), SalaryValueTypeEnum.SQL.getValue()))
//                .map(salaryItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
//                        .salaryItemId(salaryItemPO.getId())
//                        .salaryItemName(salaryItemPO.getName())
//                        .build())
//                .collect(Collectors.toList());
//        // 薪资项目中的输入项
//        List<SalaryAcctImportFieldDTO.ImportFieldDTO> inputItems = salaryItems.stream()
//                .filter(salaryItemPO -> Objects.equals(salaryItemPO.getValueType(), SalaryValueTypeEnum.INPUT.getValue()))
//                .map(salaryItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
//                        .salaryItemId(salaryItemPO.getId())
//                        .salaryItemName(salaryItemPO.getName())
//                        .build())
//                .collect(Collectors.toList());
        // 缓存勾选
        List<Long> checkItems = new ArrayList<>();
        List<SalaryAcctResultTemplatePO> salaryAcctResultTemplatePOS = getSalaryAcctResultTemplateService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId(), Arrays.asList(2));
        if (CollectionUtils.isNotEmpty(salaryAcctResultTemplatePOS)) {
            String items = salaryAcctResultTemplatePOS.get(0).getSalaryItemIds();
            checkItems = StrUtil.isNotEmpty(items) ? Arrays.stream(items.split(",")).map(Long::valueOf).collect(Collectors.toList()) : new ArrayList<>();
        }
        return SalaryAcctImportFieldDTO.builder().itemsByGroup(itemsByGroup).checkItems(checkItems).build();
    }

    @Override
    public SalaryAcctImportFieldDTO getExportField(Long salaryAcctRecordId) {
        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        SalaryAcctConfig salaryAcctConfig = getSalaryAcctSobConfigService(user).getSalaryAcctConfig(salaryAcctRecordId);

        // 查询薪资核算记录所用的薪资账套的薪资项目副本
        List<SalarySobItemPO> salarySobItems = salaryAcctConfig.getSalarySobItems();
        salarySobItems = salarySobItems.stream().filter(salarySobItemPO -> salarySobItemPO.getItemHide() == null || salarySobItemPO.getItemHide() == 0).collect(Collectors.toList());
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItems, SalarySobItemPO::getSalaryItemId);
        // 查询薪资项目
        List<SalaryItemPO> salaryItems = getSalaryItemService(user).listByIds(salaryItemIds);
        Map<Long, SalaryItemPO> salaryItemMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getId);

        Map<Long, List<SalarySobItemPO>> salarySobItemPOMap = SalaryEntityUtil.group2Map(salarySobItems, SalarySobItemPO::getSalarySobItemGroupId);

        // 查询薪资账套的薪资项目分类
        List<SalarySobItemGroupPO> salarySobItemGroupPOS = salaryAcctConfig.getSalarySobItemGroups();
        // 对分组进行排序
        salarySobItemGroupPOS = SalaryAcctResultBO.sortGroup(salarySobItemGroupPOS);
        // 对分组内薪资项目排序
        SalaryAcctResultBO.sortItem(salarySobItemPOMap);
        // 根据账套分组封装薪资项目的值
        List<SalaryAcctImportFieldDTO.ImportFieldByGroupDTO> itemsByGroup = new ArrayList<>();
        for (SalarySobItemGroupPO groupPO : salarySobItemGroupPOS) {
            List<SalarySobItemPO> groupItems = salarySobItemPOMap.getOrDefault(groupPO.getId(), Collections.emptyList());
            List<SalaryAcctImportFieldDTO.ImportFieldDTO> items = groupItems.stream()
                    .map(salarySobItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
                            .salaryItemId(salarySobItemPO.getSalaryItemId())
                            .salaryItemName(Optional.ofNullable(salaryItemMap.get(salarySobItemPO.getSalaryItemId())).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY))
                            .build())
                    .collect(Collectors.toList());

            itemsByGroup.add(SalaryAcctImportFieldDTO.ImportFieldByGroupDTO.builder()
                    .salarySobItemGroupId(groupPO.getId())
                    .salarySobItemGroupName(groupPO.getName())
                    .salaryItems(items)
                    .sortedIndex(groupPO.getSortedIndex()).build());
        }
        // 未分类
        List<SalarySobItemPO> noGroupItems = salarySobItemPOMap.getOrDefault(0L, Collections.emptyList());
        if (CollectionUtils.isNotEmpty(noGroupItems)) {
            List<SalaryAcctImportFieldDTO.ImportFieldDTO> items = noGroupItems.stream()
                    .map(salarySobItemPO -> SalaryAcctImportFieldDTO.ImportFieldDTO.builder()
                            .salaryItemId(salarySobItemPO.getSalaryItemId())
                            .salaryItemName(Optional.ofNullable(salaryItemMap.get(salarySobItemPO.getSalaryItemId())).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY))
                            .build())
                    .collect(Collectors.toList());

            itemsByGroup.add(SalaryAcctImportFieldDTO.ImportFieldByGroupDTO.builder()
                    .salarySobItemGroupId(0L)
                    .salarySobItemGroupName("未分类")
                    .salaryItems(items)
                    .sortedIndex(itemsByGroup.size()).build());
        }
        // 缓存勾选
        String cacheKey = user.getUID() + SalaryItemConstant.RESULT_EXPORT_FIELD_SIGN;
        String cacheValue = (String) Util_DataCache.getObjVal(cacheKey);
        List<Long> checkItems = JsonUtil.parseList(cacheValue, Long.class) == null ? new ArrayList<>() : JsonUtil.parseList(cacheValue, Long.class);
        // 转换成dto
//        return SalaryAcctImportFieldDTO.builder().formulaItems(formulaItems).sqlItems(sqlItems).inputItems(inputItems).checkItems(checkItems).build();
        return SalaryAcctImportFieldDTO.builder().itemsByGroup(itemsByGroup).checkItems(checkItems).build();
    }

    @Override
    public XSSFWorkbook exportImportTemplate(SalaryAcctImportTemplateParam param) {

        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(param.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }

        List<Long> checkItems = new ArrayList<>();
        List<SalaryAcctResultTemplatePO> salaryAcctResultTemplatePOS = getSalaryAcctResultTemplateService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId(), Arrays.asList(2));
        if (CollectionUtils.isNotEmpty(salaryAcctResultTemplatePOS)) {
            String items = salaryAcctResultTemplatePOS.get(0).getSalaryItemIds();
            checkItems = StrUtil.isNotEmpty(items) ? Arrays.stream(items.split(",")).map(Long::valueOf).collect(Collectors.toList()) : new ArrayList<>();
        }

        // 必须选择导入模板所需的薪资项目
        if (CollectionUtils.isEmpty(checkItems)) {
            // 查询薪资核算记录所用的薪资账套的薪资项目副本
            List<SalarySobItemPO> salarySobItems = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
            List<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItems, SalarySobItemPO::getSalaryItemId, Collectors.toList());
            if (CollectionUtils.isEmpty(salaryItemIds)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "该核算记录不存在薪资项目"));
            }
            checkItems = salaryItemIds;
        }
        param.setSalaryItemIds(checkItems);

//        // 模板表头(默认必带"个税扣缴义务人"、"姓名")
//        List<Object> headerList = Lists.newArrayList(SalaryI18nUtil.getI18nLabel(85429, "姓名"),
//                "部门",
//                SalaryI18nUtil.getI18nLabel(86186, "手机号"),
//                SalaryI18nUtil.getI18nLabel(86317, "工号"),
//                SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"));
//        List<String> dataIndexList = Lists.newArrayList("username", "departmentName", "mobile", "workcode", "taxAgentName");
//        // 查询薪资项目
//        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(param.getSalaryItemIds());
//        for (SalaryItemPO salaryItemPO : salaryItemPOS) {
//            headerList.add(salaryItemPO.getName());
//            dataIndexList.add("" + salaryItemPO.getId());
//        }
//
//        // 查询薪资核算结果
//        List<Map<String, Object>> resultMapList = getSalaryAcctResultService(user).listByParam(param);
//        // excel导出的数据
//        List<List<Object>> rows = Lists.newArrayListWithExpectedSize(resultMapList.size());
//        rows.add(headerList);
//        for (Map<String, Object> map : resultMapList) {
//            List<Object> row = Lists.newArrayListWithExpectedSize(headerList.size());
//            for (String dataIndex : dataIndexList) {
//                row.add(map.getOrDefault(dataIndex, StringUtils.EMPTY));
//            }
//            rows.add(row);
//        }
//        return ExcelUtil.genWorkbookV2(rows, sheetName);

        // 查询薪资项目
        List<Object> headerRangeList = new ArrayList<>();
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(param.getSalaryItemIds());
        for (SalaryItemPO salaryItemPO : salaryItemPOS) {
            headerRangeList.add(salaryItemPO.getId().toString());
        }
        headerRangeList.add("username");
        headerRangeList.add("mobile");
        headerRangeList.add("workcode");
        headerRangeList.add("idNo");
        headerRangeList.add("taxAgentName");
        headerRangeList.add("departmentName");
        // 查询列表的表头
        List<WeaTableColumnGroup> weaTableColumns = listWeaTableColumn(salaryAcctRecordPO);
        List<WeaTableColumnGroup> finalWeaTableColumns = new ArrayList<>();
        for (WeaTableColumnGroup tableColumn : weaTableColumns) {
            WeaTableColumnGroup columnGroupItem = (WeaTableColumnGroup) tableColumn;
            if (columnGroupItem.getChildren() != null) {
                List<WeaTableColumnGroup> childrenColumns = columnGroupItem.getChildren().stream().filter(f -> headerRangeList.contains(f.getColumn())).collect(Collectors.toList());
                if (childrenColumns.size() > 0) {
                    columnGroupItem.setChildren(childrenColumns);
                    finalWeaTableColumns.add(columnGroupItem);
                }
            } else if (headerRangeList.contains(columnGroupItem.getColumn())) {
                finalWeaTableColumns.add(columnGroupItem);
            }

        }
        // excel导出的表头
        List<WeaTableColumnGroup> headerColumnGroup = Lists.newArrayList();
        // 查询列表的表头
        parseHeader(headerColumnGroup, finalWeaTableColumns);

        List<Object> headerList = new ArrayList<>(finalWeaTableColumns);
        // excel导出的数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);
        if (param.getImportType() != null && param.getImportType().equals("true")) {
            // 需要导出现有数据，查询薪资核算结果
            param.setDynamicEmpInfo(true);
            List<Map<String, Object>> resultMapList = getSalaryAcctResultService(user).listByParam(param);
            for (Map<String, Object> map : resultMapList) {
                List<Object> row = Lists.newArrayListWithExpectedSize(headerColumnGroup.size());
                for (WeaTableColumnGroup weaTableColumn : headerColumnGroup) {
                    row.add(map.getOrDefault(weaTableColumn.getColumn(), StringUtils.EMPTY));
                }
                rows.add(row);
            }
        }


        String sheetName = "薪资核算导入模板";
        return ExcelUtilPlus.genWorkbookWithChildTitleColumn(rows, sheetName, false);
    }


    @Override
    public XSSFWorkbook exportComparisonResult(SalaryComparisonResultQueryParam queryParam) {
        ValidUtil.doValidator(queryParam);

        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        // 查询线下对比结果
        SalaryComparisonResultListDTO salaryComparisonResultListDTO = getSalaryComparisonResultService(user).listByParam(queryParam);
        // 薪资核算线下对比结果列表表头
        List<Object> headerList = Lists.newArrayList();
        // 查询薪资核算所用的薪资账套的员工信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        Set<String> employeeFieldCodeSet = SalaryEntityUtil.properties(salarySobEmpFieldPOS, SalarySobEmpFieldPO::getFieldCode);
        // 查询薪资项目
        List<SalaryItemPO> salaryItemPOS = getSalarySobItemService(user).listBySalarySobId4SalaryItem(salaryAcctRecordPO.getSalarySobId());
        Set<String> salaryItemIds = SalaryEntityUtil.properties(salaryItemPOS, salaryItemPO -> "" + salaryItemPO.getId());
        for (WeaTableColumnGroup weaTableColumnGroup : salaryComparisonResultListDTO.getWeaTableColumns()) {
            // 员工信息字段
            if (employeeFieldCodeSet.contains(weaTableColumnGroup.getColumn())) {
                headerList.add(weaTableColumnGroup.getText());
            } else {
                // 薪资项目的表头
                List<WeaTableColumnGroup> childrenList = weaTableColumnGroup.getChildren();
                if (CollectionUtils.isEmpty(childrenList)) {
                    // 是否是无分类
                    if (salaryItemIds.contains(weaTableColumnGroup.getColumn())) {
                        headerList.add(weaTableColumnGroup.getText() + " (线上值)");
                        headerList.add(weaTableColumnGroup.getText() + " (线下值)");
                    }
                } else {
                    for (WeaTableColumnGroup children : childrenList) {
                        if (salaryItemIds.contains(children.getColumn())) {
                            headerList.add(children.getText() + " (线上值)");
                            headerList.add(children.getText() + " (线下值)");
                        }
                    }
                }
            }


        }

        List<Map<String, Object>> resultMapList = salaryComparisonResultListDTO.getData().getList();
        // excel导出的数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);
        for (Map<String, Object> map : resultMapList) {
            List<Object> row = Lists.newArrayList();
            for (WeaTableColumnGroup weaTableColumnGroup : salaryComparisonResultListDTO.getWeaTableColumns()) {
                // 员工信息字段的值
                if (employeeFieldCodeSet.contains(weaTableColumnGroup.getColumn())) {
                    row.add(map.get(weaTableColumnGroup.getColumn()));
                }
                // 薪资项目的值
                List<WeaTableColumnGroup> childrenList = weaTableColumnGroup.getChildren();
                if (CollectionUtils.isEmpty(childrenList)) {
                    // 是否是无分类
                    if (salaryItemIds.contains(weaTableColumnGroup.getColumn())) {
                        Map tempMap = (Map) map.getOrDefault(weaTableColumnGroup.getColumn(), Collections.emptyMap());
                        row.add(tempMap.get("acctResultValue"));
                        row.add(tempMap.get("excelResultValue"));
                    }
                } else {
                    for (WeaTableColumnGroup children : childrenList) {
                        if (salaryItemIds.contains(children.getColumn())) {
                            Map tempMap = (Map) map.getOrDefault(children.getColumn(), Collections.emptyMap());
                            row.add(tempMap.get("acctResultValue"));
                            row.add(tempMap.get("excelResultValue"));
                        }
                    }
                }
            }
            rows.add(row);
        }

        // 记录日志
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        String targetName = salarySobPO.getName() + ":" + SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth());
        SalaryLoggerUtil.recordExportSingleLog(SalaryElogConfig.salaryAcctRecordLoggerTemplate,
                queryParam.getSalaryAcctRecordId(),
                targetName,
                SalaryI18nUtil.getI18nLabel(0, "导出线下对比结果"),
                SalaryI18nUtil.getI18nLabel(0, "导出线下对比结果") + ":" + targetName,
                user
        );

        String sheetName = "线下对比结果";

//        return ExcelUtil.genWorkbookV2(rows, sheetName);
        return ExcelUtilPlus.genWorkbookV2(rows, sheetName);
    }


    @Override
    public XSSFWorkbook exportComparisonResultTemplate(SalaryComparisonResultExportParam exportParam) {

        ValidUtil.doValidator(exportParam);

        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(exportParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        // // 模板表头(默认必带"个税扣缴义务人"、"姓名")
        // List<Object> headerList = Lists.newArrayList(SalaryI18nUtil.getI18nLabel(85429, "姓名"), SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"));
        // //定位人员带“部门”，“手机号”，“工号”
        // headerList.add("部门");
        // headerList.add("手机号");
        // headerList.add("工号");
        // 查询薪资账套下的薪资项目
        SalarySobItemAggregateDTO salarySobItemAggregateDTO = getSalarySobItemService(user).getAggregateByRecordId(exportParam.getSalaryAcctRecordId(), true);

//        // 员工信息
//        for (SalarySobEmpFieldDTO item : salarySobItemAggregateDTO.getEmpFields()) {
//            if (!"姓名".equals(item.getFieldName()) && !"个税扣缴义务人".equals(item.getFieldName())) {
//                if ("手机".equals(item.getFieldName())) {
//                    headerList.add("手机号");
//                } else {
//                    headerList.add(item.getFieldName());
//                }
//            }
//
//        }
//         // 薪资项目分类下的新资项目
//         for (SalarySobItemGroupDTO itemGroup : salarySobItemAggregateDTO.getItemGroups()) {
//             for (SalarySobItemDTO item : itemGroup.getItems()) {
//                 headerList.add(item.getName());
//             }
//         }
//         // 没有分类的薪资项目
//         for (SalarySobItemDTO item : salarySobItemAggregateDTO.getItems()) {
//             headerList.add(item.getName());
//         }


        // 无分类薪资项目id
        List<String> noGroupItemIds = salarySobItemAggregateDTO.getItems().stream().map(dto -> String.valueOf(dto.getSalaryItemId())).collect(Collectors.toList());
        // 查询列表的表头
        List<WeaTableColumnGroup> weaTableColumns = listWeaTableColumn(salaryAcctRecordPO);
        List<Object> finalWeaTableColumns = new ArrayList<>();
        // 查询薪资核算所用的薪资账套的员工信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        List<SalarySobEmpFieldDTO> salarySobEmpFieldDTOS = new SalarySobItemAggregateBO().buildEmpField(salarySobEmpFieldPOS);
        salarySobEmpFieldDTOS.stream().forEach(empField -> finalWeaTableColumns.add(new WeaTableColumnGroup("150", SalaryI18nUtil.getI18nLabel(0, empField.getFieldName()), SalaryI18nUtil.getI18nLabel(0, empField.getFieldName()))));
        for (WeaTableColumnGroup tableColumn : weaTableColumns) {
            WeaTableColumnGroup columnGroupItem = (WeaTableColumnGroup) tableColumn;
            if (columnGroupItem.getChildren() != null) {
                columnGroupItem.setChildren(columnGroupItem.getChildren());
                finalWeaTableColumns.add(columnGroupItem);
            } else if (noGroupItemIds.contains(columnGroupItem.getColumn())) {
                // 无分类
                finalWeaTableColumns.add(columnGroupItem);
            }
        }


        List<List<Object>> rows = new ArrayList<>();
        rows.add(finalWeaTableColumns);
        String sheetName = "线下对比结果导入模板";

//        return ExcelUtil.genWorkbookV2(rows, sheetName);
//        return ExcelUtilPlus.genWorkbookV2(rows, sheetName);
        return ExcelUtilPlus.genWorkbookWithChildTitleColumn(rows, sheetName, false);
    }

    public Map<String, Object> importSalaryAcctResult(SalaryAcctImportParam param) {
        return batchImport(param, "importSalaryAcctResult");
    }


    public Map<String, Object> importExcelAcctResult(SalaryAcctImportParam param) {
        return batchImport(param, "importExcelAcctResult");
    }

    @Override
    public Map<String, Object> preview(SalaryAcctImportParam param) {

        //1、参数校验
        ValidUtil.doValidator(param);

        Map<String, Object> map = new HashMap<>();

        InputStream fileInputStream = null;
        try {
            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(param.getImageId()));
            Sheet sheet = ExcelSupport.parseFile(fileInputStream, 0, EXCEL_TYPE_XLSX);
            map.put("headers", ExcelSupport.getSheetHeader(sheet, 1));
            map.put("list", ExcelParseHelper.parse2List(sheet, 2, 1));
            return map;

        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    @Override
    public void cacheImportField(SalaryAcctResultTemplateSaveParam saveParam) {
        Long salaryAcctRecordId = saveParam.getSalaryAcctRecordId();
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException("薪资核算记录不存在");
        }

        List<SalaryAcctResultTemplatePO> salaryAcctResultTemplatePOS = getSalaryAcctResultTemplateService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId(), Collections.singletonList(2));
        if (CollectionUtils.isNotEmpty(salaryAcctResultTemplatePOS)) {
            salaryAcctResultTemplatePOS.forEach(templatePO -> getSalaryAcctResultTemplateService(user).deleteById(templatePO.getId()));
        }

        Date now = new Date();
        SalaryAcctResultTemplatePO templatePO = SalaryAcctResultTemplatePO.builder()
                .id(IdGenerator.generate())
                .type(2)
                .salarySobId(salaryAcctRecordPO.getSalarySobId())
                .salaryItemIds(StringUtils.join(saveParam.getSalaryItemIds(), ","))
                .templateName("")
                .creator((long) user.getUID())
                .createTime(now)
                .updateTime(now)
                .deleteType(0)
                .build();
        getSalaryAcctResultTemplateService(user).insertIgnoreNull(templatePO);

    }

    @Override
    public void cacheExportField(List<Long> salaryItems) {
        String cacheKey = user.getUID() + SalaryItemConstant.RESULT_EXPORT_FIELD_SIGN;
        Util_DataCache.setObjVal(cacheKey, JsonUtil.toJsonString(salaryItems));
    }

    @Override
    public ExcelPreviewDTO previewImportSalaryAcctResult(SalaryAcctImportParam param) {

        //1、参数校验
        ValidUtil.doValidator(param);

        Map<String, Object> map = new HashMap<>();

        InputStream fileInputStream = null;
        try {
            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(param.getImageId()));
            Workbook workbook = ExcelSupport.parseFile(fileInputStream, EXCEL_TYPE_XLSX);
            // 创建一个公式求值器对象
            FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
            Sheet sheet = workbook.getSheetAt(0);
            return ExcelPreviewDTO.builder()
                    .headers(ExcelSupport.getSheetHeader(sheet, 1))
                    .list(ExcelParseHelper.parse2List(sheet, evaluator, 2, 1)).build();
        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    private Map<String, Object> batchImport(SalaryAcctImportParam param, String importType) {
        StopWatch stopWatch = new StopWatch("导入核算数据");
        stopWatch.start("数据准备");
        Map<String, Object> apidatas = new HashMap<String, Object>();
        ValidUtil.doValidator(param);
        Date now = new Date();

        Long currentEmployeeId = (long) user.getUID();

        //查询对于人员信息导入筛选的全局配置
        SalarySysConfPO salarySysConfPO = getSalarySysConfMapper().getOneByCode("matchEmployeeMode");
        String confValue = (salarySysConfPO != null && salarySysConfPO.getConfValue() != null && !"".equals(salarySysConfPO.getConfValue())) ? salarySysConfPO.getConfValue() : "0";

        // 薪资核算记录的id
        Long salaryAcctRecordId = param.getSalaryAcctRecordId();
        // 薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        stopWatch.stop();

        // 校验是否可以编辑
        boolean recordIsCanEdit = getSalaryApprovalRuleService(user).getRecordIsCanEdit(salaryAcctRecordPO);
        if (StringUtils.equals("importSalaryAcctResult", importType) && !recordIsCanEdit) {
            throw new SalaryRunTimeException("该核算记录已经发起审批，无法编辑或核算，请先删除审批流程");
        }
        // 薪资账套下的薪资项目副本
        stopWatch.start("薪资账套下的薪资项目副本");
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        // 薪资项目
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        List<SalaryItemPO> salaryItems = getSalaryItemService(user).listByIds(salaryItemIds);
        Map<String, Long> salaryItemMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getName, SalaryItemPO::getId);
        stopWatch.stop();
        // 薪资核算确认的人员
        stopWatch.start("薪资核算确认的人员");
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(salaryAcctRecordId);
        Map<String, SalaryAcctEmployeePO> salaryAcctEmployeeMap = SalaryEntityUtil.convert2Map(salaryAcctEmployees, e -> e.getEmployeeId() + "-" + e.getTaxAgentId());
        stopWatch.stop();
        // 租户下所有的人员
        stopWatch.start("薪资核算确认的人员");
        List<Long> employeeIds = SalaryEntityUtil.properties(salaryAcctEmployees, SalaryAcctEmployeePO::getEmployeeId, Collectors.toList());
        List<DataCollectionEmployee> salaryEmployees = getSalaryEmployeeService(user).getEmployeeByIdsAll(employeeIds);
        stopWatch.stop();
        // 租户下所有的个税扣缴义务人
        stopWatch.start("租户下所有的个税扣缴义务人");
        List<TaxAgentPO> taxAgents = getTaxAgentService(user).listAll();
        Map<String, Long> taxAgentNameMap = SalaryEntityUtil.convert2Map(taxAgents, TaxAgentPO::getName, TaxAgentPO::getId);
        stopWatch.stop();
        // 薪资核算人员信息字段
        stopWatch.start("薪资核算人员信息字段");
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        List<String> salarySobEmpFields = SalaryEntityUtil.properties(new SalarySobItemAggregateBO().buildEmpField(salarySobEmpFieldPOS), SalarySobEmpFieldDTO::getFieldName, Collectors.toList());
        List<String> excludeFields = Arrays.asList("个税扣缴义务人", "部门", "姓名");
        salarySobEmpFields = salarySobEmpFields.stream().filter(field -> !excludeFields.contains(field)).collect(Collectors.toList());
        stopWatch.stop();
        // 索引(用于计算进度)
        int index = 0;
        // 失败的数量
        int failCount = 0;
        // 成功的数量
        int successCount = 0;
        // 总数
//        int total = message.getBatchFile().getExcelSheets().stream()
//                .filter(e -> CollectionUtils.isNotEmpty(e.getHeader()) && CollectionUtils.isNotEmpty(e.getData()))
//                .map(e -> BigDecimal.valueOf(e.getData().size()))
//                .reduce(BigDecimal.ZERO, BigDecimal::add)
//                .intValue();
        // 包含错误提示信息的sheet页
        List<Map<String, String>> errorExcelSheets = Lists.newArrayList();
        // 解析excel
        List<Long> salaryAcctEmpIds = Lists.newArrayList();
        // excel导入了哪些薪资项目
        Set<Long> excelSalaryItemIds = Sets.newHashSet();
        List<SalaryAcctResultPO> salaryAcctResults = Lists.newArrayList();
        List<SalaryAcctResultReportPO> salaryAcctReports = Lists.newArrayList();
        List<SalaryAcctEmployeePO> newSalaryAcctEmployees = Lists.newArrayList();

        List<ExcelAcctResultPO> excelAcctResults = Lists.newArrayList();

        InputStream fileInputStream = null;
        try {


            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(param.getImageId()));
            stopWatch.start("解析文件");
            Workbook workbook = ExcelSupport.parseFile(fileInputStream, EXCEL_TYPE_XLSX);
            Sheet sheet = workbook.getSheetAt(0);
            stopWatch.stop();

            // 错误提示信息
            List<Map> excelComments = Lists.newArrayList();
            // 存在错误的那行数据
            List<Map<String, Object>> errorDatas = Lists.newArrayList();
            // 表头
//            List<String> headers = ExcelSupport.getSheetHeader(sheet, 0);
            List<String> headers;
//            if (StringUtils.equals("importSalaryAcctResult", importType)) {
            headers = ExcelSupport.getSheetHeader(sheet, 1);
//            } else {
//                headers = ExcelSupport.getSheetHeader(sheet, 0);
//            }

            // 处理数值
//            List<Map<String, Object>> data = ExcelParseHelper.parse2Map(sheet, 1);
            List<Map<String, Object>> data;
//            if (StringUtils.equals("importSalaryAcctResult", importType)) {
            data = ExcelParseHelper.parse2Map(sheet, 2, 1);
//            } else {
//                data = ExcelParseHelper.parse2Map(sheet, 1);
//            }

            if (CollectionUtils.isEmpty(headers)) {
                throw new RuntimeException("表头为空");
            }
            if (CollectionUtils.isEmpty(data)) {
                throw new RuntimeException("无数据");
            }
            stopWatch.start("解析数据");
            for (int i = 0; i < data.size(); i++) {

                String row = "第" + (i + 3) + "行";

                int usernameIndex = 0;
                boolean isError = false;
                Map<String, Object> map = data.get(i);
                Long employeeId = 0L;
                Long taxAgentId = 0L;
                List<SalaryAcctResultPO> salaryAcctResultsOfOneEmp = Lists.newArrayListWithExpectedSize(headers.size() - 1);
                List<SalaryAcctResultReportPO> salaryAcctResultReportOfOneEmp = Lists.newArrayListWithExpectedSize(headers.size() - 1);
                List<ExcelAcctResultPO> excelAcctResultsOfOneEmp = Lists.newArrayListWithExpectedSize(headers.size() - 1);
                for (int j = 0; j < headers.size(); j++) {
                    String header = headers.get(j);
                    String dataKey = header;
                    if (dataKey == null) {
                        continue;
                    }
                    String dataValue = (String) map.getOrDefault(dataKey.toString(), "");
                    String deparmentName = (String) map.getOrDefault("部门", "");
                    String mobile = (String) map.getOrDefault("手机号", "");
                    String workcode = (String) map.getOrDefault("工号", "");
                    String idNo = (String) map.getOrDefault("证件号码", "");

                    if (StringUtils.equals(SalaryI18nUtil.getI18nLabel(85429, "姓名"), dataKey.toString())) {
                        usernameIndex = j;
                        if (StringUtils.isEmpty(dataValue) && "0".equals(confValue)) {
                            isError = true;
                            Map<String, String> errorMessageMap = Maps.newHashMap();
                            errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(102838, "姓名不能为空"));
                            excelComments.add(errorMessageMap);
                            //salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(102838, "姓名不能为空"), i, i, j, j);
                        } else {
                            //筛选导入人员信息可以在人力资源池中匹配到的人员信息
                            List<DataCollectionEmployee> employeeSameIds = getSalaryEmployeeService(user).matchImportEmployee(confValue, salaryEmployees, dataValue, deparmentName, mobile, workcode, idNo, null);

                            if (CollectionUtils.isEmpty(employeeSameIds)) {
                                isError = true;
                                Map<String, String> errorMessageMap = Maps.newHashMap();
                                errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(100579, "姓名错误，系统内不存在该姓名"));
                                excelComments.add(errorMessageMap);
//                            salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(100579, "姓名错误，系统内不存在该姓名"), i, i, j, j);
                            } else if (employeeSameIds.size() > 1) {
                                //存在离职和在职状态取在职状态
                                employeeSameIds = employeeSameIds.stream()
                                        .filter(e -> UserStatusEnum.getNormalStatus().contains(e.getStatus()))
                                        .collect(Collectors.toList());
                                if (employeeSameIds.size() != 1) {
                                    isError = true;
                                    Map<String, String> errorMessageMap = Maps.newHashMap();
                                    errorMessageMap.put("message", row + "员工信息不存在或者存在多个员工");
                                    excelComments.add(errorMessageMap);
                                } else {
                                    employeeId = CollectionUtils.isNotEmpty(employeeSameIds) && employeeSameIds.size() == 1 ? employeeSameIds.get(0).getEmployeeId() : null;
                                }
                            } else {
                                employeeId = employeeSameIds.get(0).getEmployeeId();
                            }
                        }
                    } else if (StringUtils.equals(SalaryI18nUtil.getI18nLabel(86184, "部门"), dataKey.toString())
                            || StringUtils.equals(SalaryI18nUtil.getI18nLabel(86186, "手机号"), dataKey.toString())
                            || StringUtils.equals(SalaryI18nUtil.getI18nLabel(86186, "工号"), dataKey.toString())
                            || salarySobEmpFields.contains(dataKey.toString())
                    ) {

                    } else if (StringUtils.equals(SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"), dataKey.toString())) {
                        if (StringUtils.isEmpty(dataValue)) {
                            isError = true;
                            Map<String, String> errorMessageMap = Maps.newHashMap();
                            errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(100579, SalaryI18nUtil.getI18nLabel(102839, "个税扣缴义务人不能为空")));
                            excelComments.add(errorMessageMap);
//                        salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(102839, "个税扣缴义务人不能为空"), i, i, j, j);
                        } else {
                            taxAgentId = taxAgentNameMap.getOrDefault(dataValue, 0L);
                            if (taxAgentId == null || taxAgentId <= 0) {
                                isError = true;
                                Map<String, String> errorMessageMap = Maps.newHashMap();
                                errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(100579, SalaryI18nUtil.getI18nLabel(102840, "个税扣缴义务人错误，系统内不存在该个税扣缴义务人")));
                                excelComments.add(errorMessageMap);
//                            salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(102840, "个税扣缴义务人错误，系统内不存在该个税扣缴义务人"), i, i, j, j);
                            }
                        }
                    } else {
                        Long salaryItemId = salaryItemMap.get(dataKey.toString());
                        if (salaryItemId == null || salaryItemId <= 0) {
                            isError = true;
                            Map<String, String> errorMessageMap = Maps.newHashMap();
                            errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(100579, SalaryI18nUtil.getI18nLabel(102840, "本次核算所用账套不包含该薪资项目")));
                            excelComments.add(errorMessageMap);
//                        salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(102841, "表头错误，本次核算所用账套不包含该薪资项目"), i, i, j, j);
                        } else {
                            excelSalaryItemIds.add(salaryItemId);
                            if (StringUtils.equals("importExcelAcctResult", importType)) {
                                ExcelAcctResultPO excelAcctResult = ExcelAcctResultPO.builder()
                                        .salaryAcctRecordId(salaryAcctRecordPO.getId())
                                        .salarySobId(salaryAcctRecordPO.getSalarySobId())
                                        .salaryItemId(salaryItemId)
                                        .resultValue(dataValue)
                                        .creator(currentEmployeeId)
                                        .createTime(now)
                                        .updateTime(now)
                                        .deleteType(0)
                                        .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                                        .build();
                                excelAcctResultsOfOneEmp.add(excelAcctResult);
                            }
                            if (StringUtils.equals("importSalaryAcctResult", importType)) {
                                SalaryAcctResultPO salaryAcctResult = SalaryAcctResultPO.builder()
                                        .salaryAcctRecordId(salaryAcctRecordPO.getId())
                                        .salarySobId(salaryAcctRecordPO.getSalarySobId())
                                        .salaryItemId(salaryItemId)
                                        .resultValue(dataValue)
                                        .creator(currentEmployeeId)
                                        .createTime(now)
                                        .updateTime(now)
                                        .deleteType(0)
                                        .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                                        .build();
                                salaryAcctResultsOfOneEmp.add(salaryAcctResult);


//                                SalaryAcctResultReportPO reportPO = SalaryAcctResultReportPO.builder()
//                                        .id(IdGenerator.generate())
//                                        .salaryAcctRecordId(salaryAcctRecordPO.getId())
//                                        .salarySobId(salaryAcctRecordPO.getSalarySobId())
//                                        .salaryItemId(salaryItemId)
//                                        .resultValue(dataValue)
//                                        .creator(currentEmployeeId)
//                                        .createTime(now)
//                                        .updateTime(now)
//                                        .deleteType(0)
//                                        .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
//                                        .build();
//                                salaryAcctResultReportOfOneEmp.add(reportPO);
                            }
                        }
                    }
                    // （如果是线下对比）如果个税扣缴义务人+人员目前不在核算人员里面，不支持导入
                    if (StringUtils.equals("importExcelAcctResult", importType)
                            && (employeeId != null && employeeId > 0)
                            && (taxAgentId != null && taxAgentId > 0)
                            && j == headers.size() - 1
                            && !salaryAcctEmployeeMap.containsKey(employeeId + "-" + taxAgentId)) {
                        isError = true;
                        Map<String, String> errorMessageMap = Maps.newHashMap();
                        errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(100579, SalaryI18nUtil.getI18nLabel(102840, "本次薪资核算不包含该人员")));
                        excelComments.add(errorMessageMap);
//                    salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(102842, "本次薪资核算不包含该人员"), i, i, usernameIndex, usernameIndex);
                    }

                    // 如果个税扣缴义务人+人员目前不在核算人员里面，不支持导入
                    if (StringUtils.equals("importSalaryAcctResult", importType)
                            && (employeeId != null && employeeId > 0)
                            && (taxAgentId != null && taxAgentId > 0)
                            && j == headers.size() - 1
                            && !salaryAcctEmployeeMap.containsKey(employeeId + "-" + taxAgentId)) {
                        isError = true;
                        Map<String, String> errorMessageMap = Maps.newHashMap();
                        errorMessageMap.put("message", row + SalaryI18nUtil.getI18nLabel(100579, SalaryI18nUtil.getI18nLabel(102840, "本次薪资核算不包含该人员")));
                        excelComments.add(errorMessageMap);
//                    salaryBatchService.createExcelComment(excelComments, SalaryI18nUtil.getI18nLabel(102842, "本次薪资核算不包含该人员"), i, i, usernameIndex, usernameIndex);
                    }
                }
                // 每处理50个数据更新一次进度
                index++;
//            if (index % 50 == 0 || index >= total) {
//                salaryBatchService.sendImportRate(message.getBizId(), total, index);
//            }
                if (isError) {
                    failCount++;
                    errorDatas.add(map);
                    continue;
                }
                SalaryAcctEmployeePO salaryAcctEmployee = salaryAcctEmployeeMap.get(employeeId + "-" + taxAgentId);
                //多余的人
                if (salaryAcctEmployee == null) {
                    continue;
                }
                Long salaryAcctEmpId = Optional.ofNullable(salaryAcctEmployee).map(SalaryAcctEmployeePO::getId).orElse(0L);
                if (StringUtils.equals("importExcelAcctResult", importType)) {
                    for (ExcelAcctResultPO excelAcctResultPO : excelAcctResultsOfOneEmp) {
                        excelAcctResultPO.setEmployeeId(employeeId);
                        excelAcctResultPO.setTaxAgentId(taxAgentId);
                        excelAcctResultPO.setSalaryAcctEmpId(salaryAcctEmpId);
                    }
                    excelAcctResults.addAll(excelAcctResultsOfOneEmp);
                }
                if (StringUtils.equals("importSalaryAcctResult", importType)) {
                    if (salaryAcctEmpId <= 0) {
                        SalaryAcctEmployeePO acctEmployee = SalaryAcctEmployeePO.builder()
                                .employeeId(employeeId)
                                .salaryAcctRecordId(salaryAcctRecordId)
                                .salarySobId(salaryAcctRecordPO.getSalarySobId())
                                .salaryMonth(salaryAcctRecordPO.getSalaryMonth())
                                .taxAgentId(taxAgentId)
                                .creator(currentEmployeeId)
                                .createTime(now)
                                .updateTime(now)
                                .deleteType(0)
                                .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                                .build();
                        newSalaryAcctEmployees.add(acctEmployee);
                        salaryAcctEmpId = acctEmployee.getId();
                    }
                    for (SalaryAcctResultPO salaryAcctResultPO : salaryAcctResultsOfOneEmp) {
                        salaryAcctResultPO.setEmployeeId(employeeId);
                        salaryAcctResultPO.setTaxAgentId(taxAgentId);
                        salaryAcctResultPO.setSalaryAcctEmpId(salaryAcctEmpId);
                    }

//                    for (SalaryAcctResultReportPO po : salaryAcctResultReportOfOneEmp) {
//                        po.setEmployeeId(Util.null2String(employeeId));
//                        po.setTaxAgentId(taxAgentId);
//                        po.setSalaryAcctEmpId(Util.null2String(salaryAcctEmpId));
//
//                        DataCollectionEmployee emp = emps.get(employeeId);
//                        po.setSubcompanyId(emp.getSubcompanyid());
//                        po.setDepartmentId(emp.getDepartmentId());
//                        po.setCostcenterId(emp.getCostcenterId());
//                        po.setJobtitleId(emp.getJobtitleId());
//                        po.setLocationId(emp.getLocationId());
//
//                    }
                    salaryAcctEmpIds.add(salaryAcctEmpId);
                    salaryAcctResults.addAll(salaryAcctResultsOfOneEmp);
//                    salaryAcctReports.addAll(salaryAcctResultReportOfOneEmp);
                }
                successCount++;
            }
            stopWatch.stop();
            // 如果sheet包含错误数据
//        if (CollectionUtils.isNotEmpty(errorDatas)) {
//            salaryBatchService.createErrorExcelSheet(headers, errorDatas, excelSheet.getName(), excelComments, errorExcelSheets);
//        }
            stopWatch.start("插入数据");
            if (StringUtils.equals("importExcelAcctResult", importType)) {
                getSalaryComparisonResultService(user).deleteBySalaryAcctRecordIds(Collections.singleton(salaryAcctRecordId));
                if (CollectionUtils.isNotEmpty(excelAcctResults)) {
                    excelAcctResults.stream().forEach(result -> {
                        if (StringUtils.isEmpty(result.getResultValue())) {
                            result.setResultValue(" ");
                        }
                    });
                    getSalaryComparisonResultService(user).batchSave(excelAcctResults);
                }
            }
            if (StringUtils.equals("importSalaryAcctResult", importType)) {
                List<SalaryAcctResultPO> deleteResults = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(salaryAcctEmpIds)) {
                    // 获取删除的核算结果
                    deleteResults = getSalaryAcctResultService(user).listByAcctEmployeeIdsAndSalaryItemIds(salaryAcctEmpIds, excelSalaryItemIds);
                    getSalaryAcctResultService(user).deleteByAcctEmployeeIdsAndSalaryItemIds(salaryAcctEmpIds, excelSalaryItemIds);
                    //删除报表
//                    getSalaryAcctReportService(user).deleteByAcctEmployeeIdsAndSalaryItemIds(salaryAcctEmpIds, excelSalaryItemIds);
                }
                if (CollectionUtils.isNotEmpty(salaryAcctResults)) {
                    // 处理导入薪资项的回算值
                    handleOriginResultValue(deleteResults, salaryAcctResults);
                    // 备份一份加密前的值
                    List<SalaryAcctResultPO> list4log = new ArrayList<>();
                    salaryAcctResults.forEach(source -> {
                        SalaryAcctResultPO target = new SalaryAcctResultPO();
                        BeanUtils.copyProperties(source, target);
                        list4log.add(target);
                    });
                    getSalaryAcctResultService(user).batchSave(salaryAcctResults);

                    // 记录操作日志
                    SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
                    String targetName = salarySobPO.getName() + ":" + SalaryDateUtil.localDate2YearMonth(salaryAcctRecordPO.getSalaryMonth());
                    LoggerContext loggerContext = new LoggerContext<>();
                    loggerContext.setUser(user);
                    loggerContext.setTargetId(param.getSalaryAcctRecordId().toString());
                    loggerContext.setTargetName(targetName);
                    loggerContext.setOperateType(OperateTypeEnum.EXCEL_IMPORT.getValue());
                    loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "导入薪资核算数据"));
                    loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "导入薪资核算数据 ") + targetName);
                    loggerContext.setOldValueList(list4log);
                    SalaryElogConfig.salaryAcctRecordLoggerTemplate.write(loggerContext);

//                    //报表
//                    getSalaryAcctReportService(user).batchSave(salaryAcctReports);
                }
                if (CollectionUtils.isNotEmpty(newSalaryAcctEmployees)) {
                    getSalaryAcctEmployeeService(user).batchSave(newSalaryAcctEmployees);
                }
            }
            stopWatch.stop();
            stopWatch.start("锁定数据");
            SalarySysConfPO autoLock = getSalarySysConfService(user).getOneByCode(SalarySysConstant.EDIT_IMPORT_AUTO_LOCK);
            if (autoLock != null && StringUtils.equals(autoLock.getConfValue(), "1")) {
                // 导入的列都自动锁定
                SalaryAcctResultUpdateLockStatusParam updateLockStatusParam = SalaryAcctResultUpdateLockStatusParam.builder()
                        .salaryItemIds(excelSalaryItemIds)
                        .salaryAcctRecordId(param.getSalaryAcctRecordId())
                        .lockStatus(LockStatusEnum.LOCK).build();
                getSalaryAcctResultService(user).updateLockStatusByParam(updateLockStatusParam);
            }
            stopWatch.stop();
            log.info(stopWatch.prettyPrint());

            apidatas.put("successCount", successCount);
            apidatas.put("errorCount", failCount);
            apidatas.put("errorData", excelComments);

        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }

        // 发送导入回调信息
//        salaryBatchService.sendImportCallBackInfo(message, successCount, failCount, errorExcelSheets);
        return apidatas;
    }

    /**
     * 存储薪资核算结果数据来源日志
     *
     * @param salaryAcctResults
     */
    private void handleSalaryAcctResultLog(List<SalaryAcctResultPO> salaryAcctResults) {
        List<Long> salaryAcctEmpIds = salaryAcctResults.stream().map(SalaryAcctResultPO::getSalaryAcctEmpId).distinct().collect(Collectors.toList());
        Set<Long> salaryItemIds = salaryAcctResults.stream().map(SalaryAcctResultPO::getSalaryItemId).collect(Collectors.toSet());
        // 查询导入的薪资核算结果
        List<SalaryAcctResultPO> salaryAcctResultList = getSalaryAcctResultService(user).listByAcctEmployeeIdsAndSalaryItemIds(salaryAcctEmpIds, salaryItemIds);
        Long uid = Long.valueOf(user.getUID());
        List<SalaryAcctResultLogPO> needInsertList = SalaryAcctResultLogBO.buildSalaryAcctResultLog(salaryAcctResultList, uid, SalaryAcctResultDataSourceEnum.IMPORT);
        if (CollectionUtils.isNotEmpty(needInsertList)) {
            getSalaryAcctResultLogService(user).batchInsert(needInsertList);
        }
    }

    /**
     * @return void
     * @description
     * @author Harryxzy
     * @date 2022/12/26 22:36
     */
    private void handleOriginResultValue(List<SalaryAcctResultPO> deleteResults, List<SalaryAcctResultPO> salaryAcctResults) {
        Map<String, List<SalaryAcctResultPO>> deleteMap = SalaryEntityUtil.group2Map(deleteResults, po -> po.getSalaryAcctEmpId() + "-" + po.getSalaryItemId());
        salaryAcctResults.stream().forEach(result -> {
            List<SalaryAcctResultPO> salaryAcctResultPOS = deleteMap.get(result.getSalaryAcctEmpId() + "-" + result.getSalaryItemId());
            if (salaryAcctResultPOS != null && salaryAcctResultPOS.size() > 0) {
                result.setOriginResultValue(salaryAcctResultPOS.get(0).getOriginResultValue());
            }
        });

    }

    /**
     * 保存导出模板
     *
     * @param saveParam
     */
    @Override
    public void saveExportTemplate(SalaryAcctResultTemplateSaveParam saveParam) {
        ValidUtil.doValidator(saveParam);
        Long salaryAcctRecordId = saveParam.getSalaryAcctRecordId();
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException("薪资核算记录不存在");
        }
        saveParam.setSalarySobId(salaryAcctRecordPO.getSalarySobId());
        List<SalaryAcctResultTemplatePO> templateBySobId = getSalaryAcctResultTemplateService(user).listBySalarySobId(saveParam.getSalarySobId(), Arrays.asList(0, 1));
        if (saveParam.getId() != null) {
            Optional<SalaryAcctResultTemplatePO> saveNameOptional = templateBySobId.stream().filter(po -> po.getTemplateName().equals(saveParam.getTemplateName()) && !po.getId().equals(saveParam.getId())).findFirst();
            if (saveNameOptional.isPresent()) {
                throw new SalaryRunTimeException("该账套中已经存在同名模板，请重新设置后保存");
            }
            // 更新
            SalaryAcctResultTemplatePO salaryAcctResultTemplatePO = getSalaryAcctResultTemplateService(user).getById(saveParam.getId());
            if (salaryAcctResultTemplatePO == null) {
                throw new SalaryRunTimeException("模板不存在，或已被删除");
            }
            salaryAcctResultTemplatePO.setSalaryItemIds(StringUtils.join(saveParam.getSalaryItemIds(), ","));
            salaryAcctResultTemplatePO.setTemplateName(saveParam.getTemplateName());
            salaryAcctResultTemplatePO.setType(0);
            salaryAcctResultTemplatePO.setUpdateTime(new Date());
            getSalaryAcctResultTemplateService(user).update(salaryAcctResultTemplatePO);
        } else {
            Optional<SalaryAcctResultTemplatePO> saveNameOptional = templateBySobId.stream().filter(po -> po.getTemplateName().equals(saveParam.getTemplateName())).findFirst();
            if (saveNameOptional.isPresent()) {
                throw new SalaryRunTimeException("该账套中已经存在同名模板，请重新设置后保存");
            }
            // 新建
            Date now = new Date();
            SalaryAcctResultTemplatePO templatePO = SalaryAcctResultTemplatePO.builder()
                    .id(IdGenerator.generate())
                    .type(0)
                    .salarySobId(saveParam.getSalarySobId())
                    .salaryItemIds(StringUtils.join(saveParam.getSalaryItemIds(), ","))
                    .templateName(saveParam.getTemplateName())
                    .creator((long) user.getUID())
                    .createTime(now)
                    .updateTime(now)
                    .deleteType(0)
                    .build();
            getSalaryAcctResultTemplateService(user).insertIgnoreNull(templatePO);
        }

    }

    @Override
    public void deleteExportTemplate(List<Long> templateIds) {
        getSalaryAcctResultTemplateService(user).deleteByIds(templateIds);
    }

    @Override
    public Map<String, Object> getExportTemplateForm(Long templateId) {
        SalaryAcctResultTemplatePO salaryAcctResultTemplatePO = getSalaryAcctResultTemplateService(user).getById(templateId);
        if (salaryAcctResultTemplatePO == null) {
            throw new SalaryRunTimeException("模板不存在或已被删除");
        }
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("checkItems", StringUtils.split(salaryAcctResultTemplatePO.getSalaryItemIds(), ","));
        return resultMap;
    }

    @Override
    public Map<String, Object> exportTemplateList(SalaryAcctResultTemplateSaveParam param) {
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(param.getSalaryAcctRecordId());
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException("薪资核算记录为空");
        }
        List<SalaryAcctResultTemplatePO> salaryAcctResultTemplatePOS = getSalaryAcctResultTemplateService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId(), Arrays.asList(0, 1));
        if (StringUtils.isNotBlank(param.getTemplateName())) {
            salaryAcctResultTemplatePOS = salaryAcctResultTemplatePOS.stream().filter(po -> po.getTemplateName().contains(param.getTemplateName())).collect(Collectors.toList());
        }
        List<SalaryAcctResultTemplateDTO> resultList = new ArrayList<>();
        salaryAcctResultTemplatePOS.stream().forEach(po -> resultList.add(SalaryAcctResultTemplateDTO.builder().id(po.getId()).templateName(po.getTemplateName()).createTime(SalaryDateUtil.getFormatLocalDateTime(po.getCreateTime())).build()));
        PageInfo<SalaryAcctResultTemplateDTO> pageInfo = SalaryPageUtil.buildPage(param.getCurrent(), param.getPageSize(), resultList, SalaryAcctResultTemplateDTO.class);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("pageInfo", pageInfo);

        return resultMap;
    }

    @Override
    public XSSFWorkbook exportSalaryApprovalAcctResult(SalaryAcctResultQueryParam queryParam) {

        ValidUtil.doValidator(queryParam);
        // 审批信息
        SalaryApprovalDTO approvalInfoByRecordId = getSalaryApprovalRuleService(user).getApprovalInfoByRecordId(queryParam.getSalaryAcctRecordId());

        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }

        // 查询薪资核算结果
        List<Map<String, Object>> resultMapList = getSalaryAcctResultService(user).listByParam(queryParam);

        // 构建薪资核算结果审批列表的表头
        List<WeaTableColumnGroup> columns = Lists.newArrayList();
        // 查询薪资账套的员工信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        List<SalarySobEmpFieldDTO> salarySobEmpFieldDTOS = new SalarySobItemAggregateBO().buildEmpField(salarySobEmpFieldPOS);
        // 获取薪资项目信息
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listAll();
        Map<Long, Integer> salaryItemWidthMap = SalaryEntityUtil.convert2Map(salaryItemPOS, SalaryItemPO::getId, SalaryItemPO::getWidth);
        // 员工信息字段
        for (SalarySobEmpFieldDTO salarySobEmpFieldDTO : salarySobEmpFieldDTOS) {
            columns.add(new WeaTableColumnGroup(SalaryPageUtil.selfAdaption(salarySobEmpFieldDTO.getFieldName(), 0), salarySobEmpFieldDTO.getFieldName(), salarySobEmpFieldDTO.getFieldId()));
        }
        // 薪资项目分组下的薪资项目
        for (SalaryApprovalDTO.approvalItemGroup groupDTO : approvalInfoByRecordId.getApprovalItemGroup()) {
            if (CollectionUtils.isEmpty(groupDTO.getApprovalItems())) {
                continue;
            }
            List<WeaTableColumnGroup> childrenColumns = Lists.newArrayList();
            for (SalaryApprovalDTO.approvalItem approvalItemDTO : groupDTO.getApprovalItems()) {
                childrenColumns.add(new WeaTableColumnGroup(SalaryPageUtil.selfAdaption(approvalItemDTO.getSalaryItemName(), salaryItemWidthMap.getOrDefault(approvalItemDTO.getSalaryItemId(), 0) == null ? 0 : salaryItemWidthMap.getOrDefault(approvalItemDTO.getSalaryItemId(), 0)), approvalItemDTO.getSalaryItemName(), "" + approvalItemDTO.getSalaryItemId(), LockStatusEnum.UNLOCK.getValue().toString()));
            }
            WeaTableColumnGroup weaTableColumnWapper = new WeaTableColumnGroup(SalaryPageUtil.selfAdaption(groupDTO.getGroupName(), 0), groupDTO.getGroupName(), groupDTO.getGroupName(), childrenColumns);
            columns.add(weaTableColumnWapper);
        }
        // excel导出的表头
        List<WeaTableColumnGroup> headerColumnGroup = Lists.newArrayList();
        parseHeader(headerColumnGroup, columns);

        //合计
        boolean total = false;
        SalarySysConfPO openSum = getSalarySysConfService(user).getOneByCode(OPEN_ACCT_RESULT_SUM);
        if (openSum != null && StringUtils.isNotBlank(openSum.getConfValue()) && OpenEnum.parseByValue(openSum.getConfValue()) == OpenEnum.OPEN) {
            total = true;
            Map<String, Object> sumRow = getSalaryAcctResultService(user).sumRow(queryParam);
            sumRow.forEach((k, v) -> {
                if (NumberUtils.isCreatable(v.toString())) {
                    sumRow.put(k, new BigDecimal(v.toString()));
                }
            });
            if (sumRow != null) {
                sumRow.put("taxAgentName", "总计");
                resultMapList.add(sumRow);
            }
        }

        // excel导出的数据
        String DATA_TYPE_SUFFIX = "_type";
        List<List<Object>> rows = new ArrayList<>();
        // excel导出的表头
        List<Object> headerList = new ArrayList<>(headerColumnGroup);
        rows.add(headerList);
        for (Map<String, Object> map : resultMapList) {
            List<Object> row = Lists.newArrayListWithExpectedSize(headerColumnGroup.size());
            for (WeaTableColumnGroup weaTableColumn : headerColumnGroup) {
                String fieldType = (String) map.getOrDefault(weaTableColumn.getColumn().toString() + DATA_TYPE_SUFFIX, StringUtils.EMPTY);
                if (StringUtils.equals("number", fieldType) && map.get(weaTableColumn.getColumn()) != null && NumberUtil.isNumber(map.get(weaTableColumn.getColumn()).toString())) {
                    row.add(new BigDecimal(StringUtils.isBlank(map.get(weaTableColumn.getColumn()).toString()) ? "0" : map.get(weaTableColumn.getColumn()).toString()));
                } else {
                    row.add(map.getOrDefault(weaTableColumn.getColumn(), StringUtils.EMPTY));
                }

            }
            rows.add(row);
        }

        String sheetName = "薪资核算结果";
        return ExcelUtilPlus.genWorkbookWithChildTitleColumnWithExcelFormat(rows, sheetName, total);
    }
//    @Override
//    public Map<String, Object> exportCheckResult(
//            SalaryCheckResultExportParam exportParam,
//            ) {
//        // 查询薪资核算结果校验异常
//        List<SalaryCheckResultPO> salaryCheckResults = salaryCheckResultService.listBySalaryAcctRecordId(exportParam.getSalaryAcctRecordId());
//        List<ExcelSheetData> excelSheetData = Lists.newArrayListWithExpectedSize(salaryCheckResults.size());
//        for (SalaryCheckResultPO salaryCheckResult : salaryCheckResults) {
//            excelSheetData.add(buildDetailExcelSheetData(salaryCheckResult, simpleEmployee.getEmployeeId()));
//        }
//        ExportExcelInfo exportExcelInfo = ExportExcelInfo.builder()
//                .fileName("校验异常")
//                .dataType("校验异常")
//                .function(excelExportParam.getFunction())
//                .handlerName("salaryCheckResultHandler")
//                .flag(true)
//                .bizId(String.valueOf(excelExportParam.getBiz()))
//                .module(excelExportParam.getModule())
//                .userId(simpleEmployee.getEmployeeId())
//                .tenantKey(tenantKey)
//                .operator(simpleEmployee.getUsername())
//                .eteamsId(eteamsId)
//                .build();
//        return salaryBatchService.simpleExportExcel(exportExcelInfo, excelSheetData);
//    }
//
//    @BatchExportHandler("salaryCheckResultHandler")
//    private void salaryCheckResultHandler() {
//        BatchCallbackMessage message = BatchExportContext.getBatchCallbackMessage();
//        log.info("导出结束：{}", JsonUtil.toJsonString(message));
//    }
//
//    @Override
//    public Map<String, Object> exportCheckResultDetail(
//            Long checkResultId,
//            ) {
//        // 查询校验异常
//        SalaryCheckResultPO checkResult = salaryCheckResultService.getById(checkResultId);
//        // 查询校验异常明细
//        ExcelSheetData excelSheetData = buildDetailExcelSheetData(checkResult, simpleEmployee.getEmployeeId());
//        ExportExcelInfo exportExcelInfo = ExportExcelInfo.builder()
//                .fileName("校验异常明细")
//                .dataType("校验异常明细")
//                .function(excelExportParam.getFunction())
//                .handlerName("salaryCheckResultDetailHandler")
//                .flag(true)
//                .bizId(String.valueOf(excelExportParam.getBiz()))
//                .module(excelExportParam.getModule())
//                .userId(simpleEmployee.getEmployeeId())
//                .tenantKey(tenantKey)
//                .operator(simpleEmployee.getUsername())
//                .eteamsId(eteamsId)
//                .build();
//        return salaryBatchService.simpleExportExcel(exportExcelInfo, Collections.singletonList(excelSheetData));
//    }
//
//    @BatchExportHandler("salaryCheckResultDetailHandler")
//    private void salaryCheckResultDetailHandler() {
//        BatchCallbackMessage message = BatchExportContext.getBatchCallbackMessage();
//        log.info("导出结束：{}", JsonUtil.toJsonString(message));
//    }
//
//    private ExcelSheetData buildDetailExcelSheetData(SalaryCheckResultPO checkResult, Long employeeId) {
//        // 查询校验异常中的校验规则
//        SalarySobCheckRulePO checkRule = salarySobCheckRuleService.getById(checkResult.getSalaryCheckRuleId());
//        // 查询校验异常明细
//        List<SalaryCheckResultRecordPO> salaryCheckResultRecords = salaryCheckResultDetailService.listByCheckResultId(checkResult.getId());
//        String[] headers = new String[]{"规则名称", "规则内容", "姓名"};
//        List<List<Object>> rows = Lists.newArrayList();
//        ExcelSheetData excelSheetData = new ExcelSheetData();
//        excelSheetData.setSheetName("校验异常");
//        excelSheetData.setHeaders(Collections.singletonList(headers));
//        excelSheetData.setRows(rows);
//        if (CollectionUtils.isEmpty(salaryCheckResultRecords)) {
//            return excelSheetData;
//        }
//        List<Long> employeeIds = SalaryEntityUtil.properties(salaryCheckResultRecords, SalaryCheckResultRecordPO::getEmployeeId, Collectors.toList());
//        List<DataCollectionEmployee> simpleEmployees = hrmCommonEmployeeService.getEmployeeByIds(employeeIds);
//        Map<Long, String> simpleEmployeeMap = SalaryEntityUtil.convert2Map(simpleEmployees, DataCollectionEmployee::getEmployeeId, DataCollectionEmployee::getUsername);
//        List<ExpressFormula> expressFormulas = getSalaryFormulaService(user).listExpressFormula(Collections.singleton(checkResult.getFormulaId()));
//        Map<Long, String> formulaMap = SalaryEntityUtil.convert2Map(expressFormulas, ExpressFormula::getId, ExpressFormula::getFormula);
//        for (SalaryCheckResultRecordPO salaryCheckResultRecord : salaryCheckResultRecords) {
//            List<Object> row = Lists.newArrayListWithExpectedSize(3);
//            row.add(checkRule.getName());
//            row.add(formulaMap.getOrDefault(salaryCheckResultRecord.getFormulaId(), ""));
//            row.add(simpleEmployeeMap.getOrDefault(salaryCheckResultRecord.getEmployeeId(), ""));
//            rows.add(row);
//        }
//        return excelSheetData;
//    }
}
